library ieee;
use IEEE.STD_LOGIC_TEXTIO.all;
use STD.TEXTIO.all;
use ieee.std_logic_1164.all;


entity tb_MemoryArbiter is
	generic (Period : Time := 100 ns;
           Debug : Boolean := False);
end tb_MemoryArbiter;

architecture testbench_arch of tb_MemoryArbiter is
	
	-- component(s)
  component MemoryArbiter port (
				-- clk (neg-edge triggered), active-low reset
				clk, nReset			: in	STD_LOGIC;
				-- memory signals (in order)
				memAddr					:	out	STD_LOGIC_VECTOR(15 downto 0); -- mem address being read/written
				memWriteData		: out	STD_LOGIC_VECTOR(31 downto 0); -- mem data to be written
				memWEN					: out	STD_LOGIC; -- write enable
				memREN					: out	STD_LOGIC; -- read enable
				memReadData			: in	STD_LOGIC_VECTOR(31 downto 0); -- mem data to be read
				memState				: in	STD_LOGIC_VECTOR(1 downto 0); -- state of mem
				-- icache signals (in order)
				icacheWait			: out	STD_LOGIC; -- icache must wait if 1
				icacheREN				:	in	STD_LOGIC; -- icache read request
				icacheAddr			: in	STD_LOGIC_VECTOR(31 downto 0); -- icache address being read
				icacheReadData	: out	STD_LOGIC_VECTOR(31 downto 0); -- icache data to be read
				-- dcache signals (in order)
				dcacheREN				: in	STD_LOGIC; -- dcache read request
				dcacheWEN				: in	STD_LOGIC; -- dcache write request
				dcacheWait			:	out	STD_LOGIC; -- dcache must wait if 1
				dcacheAddr			: in	STD_LOGIC_VECTOR(31 downto 0); -- dcache address being read
				dcacheReadData	: out	STD_LOGIC_VECTOR(31 downto 0); -- dcache data to be read
				dcacheWriteData	: in	STD_LOGIC_VECTOR(31 downto 0) -- dcache data to be written
	);
  end component;

  component VarLatRAM port (
								nReset          : in std_logic ;
                clock           : in std_logic ;
                address         : in std_logic_vector (15 DOWNTO 0);
                data            : in std_logic_vector (31 DOWNTO 0);
                wren            : in std_logic ;
                rden            : in std_logic ;
                latency_override: in std_logic ; 
                q               : out std_logic_vector (31 DOWNTO 0);
                memstate        : out std_logic_vector (1 DOWNTO 0) );
  end component;
	
	-- signals
  signal clk, nReset, done	: std_logic;
  signal memState : std_logic_vector(1 downto 0);
  signal memAddr : std_logic_vector(15 downto 0);
  signal memWriteData, memReadData, icacheAddr, dcacheAddr, icacheReadData, dcacheReadData, dcacheWriteData : std_logic_vector(31 downto 0);
  signal memWEN, memREN, icacheWait, icacheREN, icacheWEN, dcacheREN, dcacheWEN, dcacheWait : std_logic;
	
	-- constants
  constant zero_v : std_logic_vector := x"00000000";
	
	-- print procedures
  procedure println( output_string : in string ) is
    variable lout                  :    line;
  begin
    WRITE(lout, output_string);
    WRITELINE(OUTPUT, lout);
  end println;

  procedure printlv( output_bv : in std_logic_vector ) is
    variable lout              :    line;
  begin
    WRITE(lout, output_bv);
    WRITELINE(OUTPUT, lout);
  end printlv;


begin

	-- DUT
  DUT : MemoryArbiter
  	port map (
			clk 						=> 	clk,
			nReset		    	=>	nReset,
			memAddr         =>	memAddr,
			memWriteData    =>	memWriteData,
			memWEN          =>	memWEN,
			memREN          =>	memREN,
			memReadData     =>	memReadData,
			memState        =>	memState,
			icacheWait      =>	icacheWait,
			icacheREN       =>	icacheREN,
			icacheAddr      =>	icacheAddr,
			icacheReadData  =>	icacheReadData,
			dcacheREN       =>	dcacheREN,
			dcacheWEN       =>	dcacheWEN,
			dcacheWait      =>	dcacheWait,
			dcacheAddr      =>	dcacheAddr,
			dcacheReadData  =>	dcacheReadData,
			dcacheWriteData =>	dcacheWriteData
		);

		Memory : VarLatRAM
  	port map (
  		nReset           =>	nReset,
      clock            =>	clk,
      address          =>	memAddr,
      data             =>	memWriteData,
      wren             =>	memWEN,
      rden             =>	memREN,
      latency_override =>	'0',
      q                =>	memReadData,
      memstate         =>	memState
		);
	
	-- generate clock signal
  clkgen: process
    variable clk_tmp : std_logic := '0';
  begin
    clk_tmp := not clk_tmp;
    clk <= clk_tmp;
    wait for Period/2;
  end process;
	
	-- TESTER
  process
  begin
  	done <= '0';
    println("");
    println("Starting Test");
    -- reset
    nReset <= '0';
    wait for Period/2;
    wait for 1 ns;
    nReset <= '1';
    icacheREN <= '0';
    icacheAddr <= x"00000001";
    dcacheREN <= '0';
    dcacheWEN <= '0';
    dcacheAddr <= x"00000001";
    dcacheWriteData <= x"DEADBEEF";

    wait for Period;

    dcacheREN <= '1'; -- dcache read

		wait for 1 ns;
    --if( dcacheWait='1') then
    	wait until dcacheWait='0';
    --end if;
    wait for Period;
    wait for 1 ns;
    dcacheWEN <= '1'; -- dcache write
    wait for 1 ns;
    dcacheREN <= '0';
    
    wait for 1 ns;
    --if( dcacheWait='1') then
    	wait until dcacheWait='0';
		--end if;
    wait for Period;
    icacheREN <= '1'; -- icache read
    wait for 1 ns;
    dcacheWEN <= '0';

    wait for 1 ns;
    --if( icacheWait='1') then
    	wait until icacheWait='0';
		--end if;
    wait for Period;
    wait for 1 ns;
    dcacheREN <= '1'; -- precedence of dcache read over icache read

    wait for 1 ns;
    --if( dcacheWait='1') then
    	wait until dcacheWait='0';
		--end if;
    wait for Period;
    wait for 1 ns;
    dcacheREN <= '0';
    dcacheWriteData <= x"EEEEEEEE";
    dcacheWEN <= '1'; -- precedence of dcache write over icache read

    wait for 1 ns;
    --if( dcacheWait='1') then
    	wait until dcacheWait='0';
		--end if;
    wait for Period;
    wait for 1 ns;
    dcacheWEN <= '0'; -- icache still read after being preceeded

    wait for 1 ns;
    --if( icacheWait='1') then
    	wait until icacheWait='0';
		--end if;
    Wait for Period;
    wait for 1 ns;
    icacheREN <= '0'; -- cleanup
    

    println("Test Complete");
    println("");
    -- end simulation
    done <= '1';
    wait;
  end process;
  
end testbench_arch;


